﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace Nirvana
{
	

	[CreateAssetMenu(fileName = "LuaCheckRule", menuName = "Nirvana/Asset/LuaCheckRule")]
    public class LuaCheckRule : ScriptableObject
    {
		private Regex tmpRegex;

        [SerializeField]
        [Tooltip("The regex for include assets.")]
        private ListStringVariate includeRegexs;

        [SerializeField]
        [Tooltip("The regex for exclude assets.")]
        private ListStringVariate excludeRegexs;


		private enum LuaStdMode
		{
			[EnumLabel("Default")]
			Default,
			[EnumLabel("Lua51")]
			Lua51,
			[EnumLabel("Lua51c")]
			Lua51c,
			[EnumLabel("Lua52")]
			Lua52,
			[EnumLabel("Lua52c")]
			Lua52c,
			[EnumLabel("Lua53")]
			Lua53,
			[EnumLabel("Lua53c")]
			Lua53c,
			[EnumLabel("LuaJit")]
			LuaJit,
			[EnumLabel("NgxLua")]
			NgxLua,
			[EnumLabel("Min")]
			Min,
			[EnumLabel("Max")]
			Max,
			[EnumLabel("Love")]
			Love,
			[EnumLabel("Busted")]
			Busted,
			[EnumLabel("Rockspec")]
			Rockspec,
			[EnumLabel("None")]
			None
		}

		[SerializeField]
		[EnumLabel]
		[Tooltip("设置Lua编译标准")]
		private LuaStdMode luaStd;

		[Header("过滤掉与全局变量相关的警告.")]
		[SerializeField]
		private bool noGlobal;

		[SerializeField]
		[Header("定义额外的全局变量和字段.")]
		private ListStringVariate globals;

		[Header("添加只读全局变量或字段.")]
		[SerializeField]
		private ListStringVariate readGlobals;

		[SerializeField]
		[Header("筛选出与未使用的变量和值相关的警告.")]
		private bool noUnused;

		[Header("过滤掉与重新定义的变量相关的警告.")]
		[SerializeField]
		private bool noRedefined;

		[SerializeField]
		[Header("筛选出与未使用的参数和循环变量相关的警告.")]
		private bool noUnusedArgs;

		[SerializeField]
		[Header("过滤掉与未使用的变量和已使用的变量一起设置的警告.")]
		private bool noUnusedSecondaries;

		[Header("筛选出与隐式参数相关的警告.")]
		[SerializeField]
		private bool noSelf;

		[SerializeField]
		[Header("设置以代码结尾的行的最大允许长度（-1不限制长度）.")]
		private int maxCodeLineLength = -1;

		[Header("设置字符串中行的最大允许长度（-1不限制长度）.")]
		[SerializeField]
		private int maxStringLineLength = -1;

		[SerializeField]
		[Header("设置注释行允许的最大长度（-1不限制长度）.")]
		private int maxCommentLineLength = -1;

		[Header("筛选出与模式匹配的警告.")]
		[SerializeField]
		private ListStringVariate ignores;

		[HideInInspector]
		[Tooltip("The check reports.")]
		[SerializeField]
		private List<LuaCheckReportItem> reports = new List<LuaCheckReportItem>();
		public List<LuaCheckReportItem> Reports { get { return reports; } }


		public static LuaCheckRule[] GetAllLuaCheckRule()
		{
			string[] array = AssetDatabase.FindAssets("t:LuaCheckRule");
			LuaCheckRule[] array2 = new LuaCheckRule[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i];
				string text2 = AssetDatabase.GUIDToAssetPath(text);
				array2[i] = AssetDatabase.LoadAssetAtPath<LuaCheckRule>(text2);
			}
			return array2;
		}

		public string GetDirectorName()
		{
			string assetpath = AssetDatabase.GetAssetPath(this);
			return Path.GetDirectoryName(assetpath);
		}

		public int GetTotalCount()
		{
			string assetpath = AssetDatabase.GetAssetPath(this);
			string dirname = Path.GetDirectoryName(assetpath);
			var allasset = AssetDatabase.FindAssets("", new string[] { dirname });
			allasset = allasset.RemoveDuplicate<string>();
			return allasset.Length;
		}

		public bool Progress(ProgressIndicator progress)
        {
			if (this.reports == null) this.reports = new List<LuaCheckReportItem>();
			else this.reports.Clear();

			if (!includeRegexs.IsHasValue()) return false;

			if (!LuaCheckTool.IsExist())
			{
				EditorUtility.DisplayDialog("Lua Check Failed", "未找到LuaCheck.exe执行文件：" + LuaCheckTool.GetAppPath()
					+ ", 请在Perferences/Nirvana Tools面板里面选中exe", "I Known");
				return false;
			}

			Regex[] inRegexs = null;
			if (includeRegexs.IsHasValue())
			{
				var temparray = this.includeRegexs.GetValue().ToArray();
				if (temparray != null && temparray.Length > 0)
				{
					inRegexs = new Regex[temparray.Length];
					for (int i = 0; i < temparray.Length; i++) inRegexs[i] = new Regex(temparray[i]);
				}
			}

			Regex[] exRegexs = null;
			if (excludeRegexs.IsHasValue())
			{
				var temparray = this.excludeRegexs.GetValue().ToArray();
				if (temparray != null && temparray.Length > 0)
				{
					exRegexs = new Regex[temparray.Length];
					for (int i = 0; i < temparray.Length; i++) exRegexs[i] = new Regex(temparray[i]);
				}
			}

			string dirname = this.GetDirectorName();
			dirname = dirname.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
			string[] allguids = AssetDatabase.FindAssets(string.Empty, new string[]
			{
				dirname
			});
			allguids = allguids.RemoveDuplicate<string>();

			foreach (var p in allguids)
			{
				string assetpath = AssetDatabase.GUIDToAssetPath(p);
				if (progress.Show("Check {0}", assetpath)) return true;

				progress.AddProgress(1f);
				if(Path.GetExtension(assetpath) == ".lua")
				if (!AssetDatabase.IsValidFolder(assetpath))
				{
					string input = DirectoryUtil.RelativePath(dirname, assetpath);
					if (inRegexs != null)
					{
						bool flag = false;
						foreach (Regex r in inRegexs)
						{
							if (r.IsMatch(input))
							{
								flag = true;
							}
						}
						if (!flag)
						{
							return false;
						}
					}
					if (exRegexs != null)
					{
						bool flag2 = false;
						foreach (Regex r in exRegexs)
						{
							if (r.IsMatch(input))
							{
								flag2 = true;
								break;
							}
						}
						if (flag2)
						{
							return false;
						}
					}
					CheckRule(assetpath);
				}
			}
			return false;
		}

		private bool CheckRule(string assetpath)
        {
			StringBuilder stringBuilder = new StringBuilder();
			switch (this.luaStd)
			{
				case LuaStdMode.Lua51:
					stringBuilder.Append("--std lua51 "); //-std指编译器编译时采用的lua51标准
					break;
				case LuaStdMode.Lua51c:
					stringBuilder.Append("--std lua51c ");
					break;
				case LuaStdMode.Lua52:
					stringBuilder.Append("--std lua52 ");
					break;
				case LuaStdMode.Lua52c:
					stringBuilder.Append("--std lua52c ");
					break;
				case LuaStdMode.Lua53:
					stringBuilder.Append("--std lua53 ");
					break;
				case LuaStdMode.Lua53c:
					stringBuilder.Append("--std lua53c ");
					break;
				case LuaStdMode.LuaJit:
					stringBuilder.Append("--std luajit ");
					break;
				case LuaStdMode.NgxLua:
					stringBuilder.Append("--std ngx_lua ");
					break;
				case LuaStdMode.Min:
					stringBuilder.Append("--std min ");
					break;
				case LuaStdMode.Max:
					stringBuilder.Append("--std max ");
					break;
				case LuaStdMode.Love:
					stringBuilder.Append("--std love ");
					break;
				case LuaStdMode.Busted:
					stringBuilder.Append("--std busted ");
					break;
				case LuaStdMode.Rockspec:
					stringBuilder.Append("--std rockspec ");
					break;
				case LuaStdMode.None:
					stringBuilder.Append("--std none ");
					break;
			}

			if (this.noGlobal)
			{
				stringBuilder.Append("--no-global ");
            }
            else
            {
	
				if (this.globals.IsHasValue())
                {
					stringBuilder.Append("--globals ");
					var temparray = this.globals.GetValue().ToArray();
					foreach (string s in temparray)
					{
						stringBuilder.Append(s);
						stringBuilder.Append(' ');
					}
				}
				if (this.readGlobals.IsHasValue())
				{
					stringBuilder.Append("--read-globals ");
					var temparray = this.readGlobals.GetValue().ToArray();
					foreach (string s in temparray)
					{
						stringBuilder.Append(s);
						stringBuilder.Append(' ');
					}
				}
			}
			if (this.noUnused)
			{
				stringBuilder.Append("--no-unused ");
			}
			if (this.noRedefined)
			{
				stringBuilder.Append("--no-redefined ");
			}
			if (this.noUnusedArgs)
			{
				stringBuilder.Append("--no-unused-args ");
			}
			if (this.noUnusedSecondaries)
			{
				stringBuilder.Append("--no-unused-secondaries ");
			}
			if (this.noSelf)
			{
				stringBuilder.Append("--no-self ");
			}
			if (this.maxCodeLineLength >= 0)
			{
				stringBuilder.Append("--max-code-line-length=");
				stringBuilder.Append(this.maxCodeLineLength);
				stringBuilder.Append(' ');
			}
			else
			{
				stringBuilder.Append("--no-max-code-line-length ");
			}
			if (this.maxStringLineLength >= 0)
			{
				stringBuilder.Append("--max-string-line-length=");
				stringBuilder.Append(this.maxCodeLineLength);
				stringBuilder.Append(' ');
			}
			else
			{
				stringBuilder.Append("--no-max-string-line-length ");
			}
			if (this.maxCommentLineLength >= 0)
			{
				stringBuilder.Append("--max-comment-line-length=");
				stringBuilder.Append(this.maxCodeLineLength);
				stringBuilder.Append(' ');
			}
			else
			{
				stringBuilder.Append("--no-max-comment-line-length ");
			}
			if (this.ignores.IsHasValue() && this.ignores.GetValue().Count>0)
			{
				stringBuilder.Append("--ignore ");
				var temparray = this.ignores.GetValue().ToArray();
				foreach (string s in temparray)
				{
					stringBuilder.Append(s);
					stringBuilder.Append(' ');
				}
			}
			LuaCheckTool.StartApplication(assetpath, stringBuilder.ToString(), OnLuaCheckComplete);
			return true;
        }

		private void OnLuaCheckComplete(string servertype,string msg)
        {
			if(tmpRegex == null) tmpRegex = new Regex("^(.*).lua:(.*):(.*?):(.*)");

			Match match = tmpRegex.Match(msg);
			if(match.Success && match.Groups.Count > 4)
            {
				LuaCheckReportItem p = new LuaCheckReportItem();
				p.Path = match.Groups[1].Value + ".lua";
				p.Row = int.Parse(match.Groups[2].Value);
				p.Column = int.Parse(match.Groups[3].Value);
				p.Message = match.Groups[4].Value;
				p.Severity = servertype == "warn" ? LuaCheckReportItem.ServerityType.Warn : LuaCheckReportItem.ServerityType.Error;
				this.reports.Add(p);
			}

		}
	}
}

